package com.pine.dcts.exception;


import com.pine.app.module.security.oauth.exception.AuthenticationException;
import com.pine.dcts.common.DctsConstant;
import com.pine.dcts.common.DctsException;
import com.pine.dcts.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.beans.factory.annotation.Value;

import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 统一异常处理器
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@Slf4j
public class AppExceptionHandler {

    @Value("${spring.profiles.active:dev}")
    public String activeProfile;

    private String getMessage(Exception exception, HttpStatus status) {
        if (activeProfile.equalsIgnoreCase(DctsConstant.SYSTEM_MODE.DEVELOP) || activeProfile.equalsIgnoreCase(DctsConstant.SYSTEM_MODE.TEST)) {
            return StringUtils.isNotBlank(exception.getMessage()) ? exception.getMessage() : status.getMessage();
        }
        return status.getMessage();

    }

    /**
     * 构建返回错误
     *
     * @param status
     * @param exception
     * @param data
     * @return
     */
    private Result failure(HttpServletRequest request, HttpStatus status, Exception exception, Object data) {
        String meesage = status.getMessage();
        if (exception != null) {
            //根据是否是开发或者测试模式，改变提示消息
            meesage = this.getMessage(exception, status);
        }
        return Result.error(status.value(), meesage);
    }


    private Result exceptionHandler(HttpServletRequest request, HttpStatus status, Exception e, Object data) {
       log.error(status.getMessage() + ":" + e.getMessage(), e);
        return failure(request, status, e, data);
    }

    private Result exceptionHandler(HttpServletRequest request, HttpStatus status, Exception e) {
        return exceptionHandler(request, status, e, null);
    }

    /**
     * @Description: 数据错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ResponseStatus(org.springframework.http.HttpStatus.OK)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
        FieldError fieldError = e.getBindingResult().getFieldError();
        return exceptionHandler(request, HttpStatus.ILLEGAL_ARGUMENT,
                new DctsException(HttpStatus.ILLEGAL_ARGUMENT.value(),
//                        "[" + fieldError.getField() + "]" +
                                fieldError.getDefaultMessage()));
    }

    /**
     * @Description: 数据错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ResponseStatus(org.springframework.http.HttpStatus.OK)
    @ExceptionHandler(ConstraintViolationException.class)
    public Result handleValidationException(HttpServletRequest request, ConstraintViolationException e) {
        List<Map<String, Object>> fields = new ArrayList<>();
        for (ConstraintViolation<?> cv : e.getConstraintViolations()) {
            String fieldName = ((PathImpl) cv.getPropertyPath()).getLeafNode().asString();
            String message = cv.getMessage();
            Map<String, Object> field = new HashMap<>();
            field.put("field", fieldName);
            field.put("message", message);
            fields.add(field);
        }
        Map<String, Object> map = fields.get(0);
        return exceptionHandler(request, HttpStatus.ILLEGAL_ARGUMENT,
                new DctsException(HttpStatus.ILLEGAL_ARGUMENT.value(),
                        "" +
                                map.get("message")), fields);
    }

    /**
     * @Description: 数据错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ExceptionHandler(BindException.class)
    @ResponseStatus(org.springframework.http.HttpStatus.OK)
    public Result handleBindException(HttpServletRequest request, BindException e) {
        List<Map<String, Object>> fields = new ArrayList<>();
        for (FieldError error : e.getFieldErrors()) {
            Map<String, Object> field = new HashMap<>();
            field.put("field", error.getField());
            field.put("message", error.getDefaultMessage());
            fields.add(field);
        }
        Map<String, Object> map = fields.get(0);
        return exceptionHandler(request, HttpStatus.ILLEGAL_ARGUMENT,
                new DctsException(HttpStatus.ILLEGAL_ARGUMENT.value(),
                        "" + map.get("message")), fields);
    }

    /**
     * @Description: 文件太大
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ExceptionHandler(MultipartException.class)
    @ResponseStatus(org.springframework.http.HttpStatus.OK)
    public Result handleMultipartException(HttpServletRequest request, MultipartException e) {
        return exceptionHandler(request, HttpStatus.MULTIPART_TOO_LARGE, e);
    }

    /**
     * @Description: 缺少参数
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(org.springframework.http.HttpStatus.OK)
    public Result handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException e) {
        return exceptionHandler(request, HttpStatus.ILLEGAL_ARGUMENT, e);
    }

    /**
     * @Description: 参数解析失败
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(org.springframework.http.HttpStatus.OK)
    public Result httpMessageNotReadableException(HttpServletRequest request,Exception e) {
        return exceptionHandler(request, HttpStatus.ARGUMENT_NOT_READABLE, e);
    }

    /**
     * @Description: 缺少参数
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(org.springframework.http.HttpStatus.OK)
    public Result handleMissingServletRequestParameterException(HttpServletRequest request,
                                                                MissingServletRequestParameterException e) {
        return exceptionHandler(request, HttpStatus.MISSING_ARGUMENT, e);
    }

    /**
     * @Description: 参数类型错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(org.springframework.http.HttpStatus.OK)
    public Result handleMethodArgumentTypeMismatchExceptionException(HttpServletRequest request,
                                                                     MethodArgumentTypeMismatchException e) {
        return exceptionHandler(request, HttpStatus.ILLEGAL_ARGUMENT_TYPE, e);
    }

    /**
     * @Description: 未知错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:12
     */
    @ResponseBody
    @ExceptionHandler(AuthenticationException.class)
    @ResponseStatus(org.springframework.http.HttpStatus.UNAUTHORIZED)
    public Result handleException(HttpServletRequest request, AuthenticationException e) {
        return exceptionHandler(request, HttpStatus.NO_AUTH, e);
    }

    /**
     * @Description: 未知错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:12
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    @ResponseStatus(org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR)
    public Result handleException(HttpServletRequest request, Exception e) {
        return exceptionHandler(request, HttpStatus.INTERNAL_SERVER_ERROR, e);
    }

    /**
     * @Description: 不支持的方法
     * @Author: xyjxust
     * @Date: 2019/1/24 11:12
     */
    @ResponseBody
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(org.springframework.http.HttpStatus.METHOD_NOT_ALLOWED)
    public Result handleHttpRequestMethodNotSupportedException(HttpServletRequest request, HttpRequestMethodNotSupportedException e) {
        return exceptionHandler(request, HttpStatus.METHOD_NOT_ALLOWED, e);
    }

    /**
     * @Description: 非法状态
     * @Author: xyjxust
     * @Date: 2019/1/24 11:11
     */
    @ResponseBody
    @ExceptionHandler(IllegalStateException.class)
    @ResponseStatus(org.springframework.http.HttpStatus.FORBIDDEN)
    public Result handleIllegalStateException(HttpServletRequest request, IllegalStateException e) {
        return exceptionHandler(request, HttpStatus.ILLEGAL_STATE, e);
    }

    /**
     * @Description: 业务错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:04
     */
    @ResponseBody
    @ExceptionHandler(DctsException.class)
    @ResponseStatus(org.springframework.http.HttpStatus.OK)
    public Result handleServiceException(DctsException e) {
        log.warn(HttpStatus.BUSSINESS_ERROR.getMessage() + ":" + e.getMessage());
        if (e.getCode() == null) {
            return Result.error(HttpStatus.BUSSINESS_ERROR.value(), e.getMessage());
        }
        return Result.error(e.getCode(), e.getMessage());

    }
    @ResponseBody
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    @ResponseStatus(org.springframework.http.HttpStatus.OK)
    public Result handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e)
    {
        return Result.error(500,"文件大小超过限制");
    }
}
